package gitobject;

import core.JitMap;
import sha1.SHA1;

import java.lang.*;
import java.io.*;
import java.util.*;


public class Tree extends GitObject{

    private static final long serialVersionUID = -6243666545905067797L;
    protected transient ArrayList<GitObject> treeList;	//GitObjects in tree. Transient avoid serialize.
    public static String treeSeparator = "|";
    protected static String showPath = "";
    public ArrayList<GitObject> getTreeList(){
        return treeList;
    }
    public Tree(){};

    /**
     * Constructor
     * @param name
     */
    public Tree(String name){
        this.treeList = new ArrayList<>();
        this.fmt = "tree";
        this.mode = "040000";
        this.value = "";
        this.name = name;
    }

    /**
     * Constructor
     * @param file
     * @throws Exception
     */
    /**********No use************/
    public Tree(File file) throws Exception {
        this.treeList = new ArrayList<>();
        this.fmt = "tree";
        this.mode = "040000";
        this.value = "";
        this.name = file.getName();
        this.key = genKey(file);
        //compressWrite();
    }
    /****************************/
    public Vector<String[]> readValue(){
        String[] resultByLines = value.split("\n");
        Vector<String[]> result = new Vector<>();
        for (String resultByLine : resultByLines)
            result.add(resultByLine.split("\\s+", 4));
        return result;
    }

    public static void sortValue(Vector<String[]> objectValue){
        objectValue.sort(new Comparator<String[]>() {
            @Override
            public int compare(String[] o1, String[] o2) {
                return o1[3].compareTo(o2[3]);
            }
        });
    }

    public void writeValue(Vector<String[]> objectValue){
        value = "";
        for(String[] line: objectValue){
            int length = line.length;
            for(int i=0; i<length; i++){
                value += line[i];
                if(i<length - 1)
                    value += " ";
            }
            value += "\n";
        }
    }

    private String readIndex(String content){
        String blobString = content.substring(0,52);
        String blobPath = content.substring(82);
        int separator = blobPath.lastIndexOf(Tree.treeSeparator);
        String blobName = blobPath.substring(separator == -1 ? 0 : separator + Tree.treeSeparator.length());
        return blobString + " " + blobName;
    }

    /**
     * Add blob value from index content.
     * @param indexContent
     */
    public void addBlobFromIndex(String indexContent){
        String content = readIndex(indexContent);
        value += content + "\n";
    }

    /**
     * Add Object for Tree Object, pre sort required.
     * @param path
     * @throws Exception
     */
    public Tree makeTree(String path) throws Exception {
        //
        Tree finalTree = this;
        if(path.contains(File.separator)){
            String newSubName = path.substring(0, path.indexOf(File.separator));
            String newPath = path.substring(newSubName.length() + File.separator.length());
            Tree subTree = null;
            if(treeList.size() == 0 || !Objects.equals(treeList.get(treeList.size() - 1).name, newSubName)){
                subTree = new Tree(newSubName);
                treeList.add(subTree);
            }
            else subTree = (Tree)treeList.get(treeList.size() - 1);
            finalTree = subTree.makeTree(newPath);
            //updateValueForTree(contains(valueString, name), valueString, subTree);
        }
        return finalTree;
    }

    public String buildTree() {
        showPath = showPath + name + File.separator;
        if(!treeList.isEmpty()) {
            for (GitObject subObject : treeList){
                if(Objects.equals(subObject.getFmt(), "tree")) {
                    Tree subTree = (Tree)subObject;
                    subTree.buildTree();
                    value += subTree.toString() + " " + subTree.name + "\n";
                }
            }
        }
        //System.out.print(value);
        if(!Objects.equals(value, "")) {
            Vector<String[]> valueString = readValue();
            sortValue(valueString);
            writeValue(valueString);
            System.out.println(showPath);
            for(String[] line : valueString){
                String mode = line[0];
                String fmt = line[1];
                String id = line[2];
                String name = line[3];
                String status = "update";
                System.out.println(status + " " + mode + " " + id + " " + "\"" + name + "\"");
            }
            System.out.println();
        }
        try {
            key = SHA1.getHash(this.value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(!Objects.equals(value, "")){
            try {
                writeTree();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        int exPath= (File.separator + name).length();
        showPath = showPath.substring(0,showPath.length() - exPath);
        return key;
    }

    /**
     * Write this blob to commit history.
     * @throws Exception
     */
    public void writeTree() throws Exception{
        compressWrite();
    }
    /**
     * Deserialize a tree object with treeId and its path.
     * @param Id
     * @param Id
     * @throws IOException
     */
    public static Tree deserialize(String Id) throws IOException {
        Tree tree = null;
        try{
            /**
             * Todo: Add your code here.
             */
            byte[] bytes = deCompress(Id);
            if(bytes == null) return null;
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            ObjectInputStream file = new ObjectInputStream(in);
            tree = (Tree)file.readObject();
            file.close();
        }catch (Exception e){
            e.printStackTrace();
        }
        /* Todo: You should delete the return statement before you start. */
        return tree;
    }

    public int contains(Vector<String[]> objectValue, String name){
        int length = objectValue.size();
        for(int i=0; i<length; i++){
            if(Objects.equals(objectValue.get(i)[3], name))
                return i;
        }
        return -1;
    }

    /**
     * Sort the files in a certain order. You should consider whether it's a file or a directory.
     * @param fs
     * @return List
     */
    public List<File> sortFile(File[] fs){
        List<File> fileList = Arrays.asList(fs);
        fileList.sort(new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                /* Todo: Add your code here. */
                if (o1.isDirectory() && o2.isFile()) {
                    return -1;
                }
                if (o1.isFile() && o2.isDirectory()) {
                    return 1;
                }
                return o1.getName().compareTo(o2.getName());
            }
        });
        return fileList;
    }

    /**
     * Generate the key of a tree object.
     * @param dir
     * @return String
     * @throws Exception
     */
    /*************No use*****************/
    public String genKey(File dir) throws Exception{
        File[] fs = dir.listFiles();
        List<File> fileList = sortFile(fs);
        for(File item: fileList) {
            if(item.getName().equals(".jit")) continue;
            if(item.isFile()) {
                Blob blob = new Blob(item);
                blob.writeBlob();
                this.value += blob.toString() + " " + item.getName() + "\n";
                this.treeList.add(blob);
            }
            else if(item.isDirectory()) {
                Tree tree = new Tree(item);
                tree.writeTree();
                this.value += tree.toString() + " " + item.getName() + "\n";
                this.treeList.add(tree);
            }
        }
        String key = SHA1.getHash(this.value);
        return key;
    }
    @Override
    public String toString(){
        return "040000 tree " + key;
    }

}
